// 画布
const oCan = document.getElementById('can');
// canvas上下文
const ctx = oCan.getContext('2d');
// 屏幕宽度
const clientWidth = document.documentElement.clientWidth;
// 屏幕高度
const clientHeight = document.documentElement.clientHeight;

// 将画布占满整个屏幕
oCan.width = clientWidth;
oCan.height = clientHeight;

// 颜色
const oColorInput = document.getElementById('colorInput');
// 线宽
const oLineWidthRange = document.getElementById('lineWidthRange');
const oLineWidthRangeValue = document.getElementsByClassName('j-line-width')[0];
// 清除画布按钮
const oClearBtn = document.getElementById('clearAllBtn');
// 橡皮擦按钮
const oEraserBtn = document.getElementById('eraserBtn');
// 橡皮擦线宽数值条
const oEraserLineWidthRange = document.getElementById('eraserLineWidthRange');
const oEraserLineWidthRangeValue = document.getElementsByClassName('j-eraser-line-width')[0];
// 橡皮擦圆圈
const oEraserCircle = document.getElementById('eraserCircle');

// 撤销按钮
const oUndoBtn = document.getElementById('undoBtn');
// 重画按钮
const oRedoBtn = document.getElementById('redoBtn');

// 画笔初始位置
let initPos = null;
// 橡皮擦工作状态
let eraserStatus = false;

// undo时，将里面的最后一项移到revokedData
let drewData = [];
// redo时，将里面的最后一项移到drewData
let revokedData = [];

// canvas初始值
const CANVAS_VALUES = {
  DEFAULT_COLOR: '#424242',
  DEFAULT_LINE_STYLE: 'round',
  DEFAULT_LINE_WIDTH: 1,
  ERASER_COLOR: '#fff'
}

// 撤销与还原按键
const KEYBOARD = {
  // 撤销
  UNDO: 'z',
  // 还原
  REDO: 'b'
}

// const DATA_FIELD = {
//   DREW: 'drewData',
//   REVOKED: 'revokedData'
// }

// const DATA_TYPE = {
//   MOVE_TO: 'moveTo',
//   LINE_TO: 'lineTo'
// }

const init = () => {
  initStyle();
  bindEvent();
}

// 给canvas设置初始值
function initStyle () {
  ctx.setColor(CANVAS_VALUES.DEFAULT_COLOR);
  ctx.setLineStyle(CANVAS_VALUES.DEFAULT_LINE_STYLE);
  ctx.setLineWidth(CANVAS_VALUES.DEFAULT_LINE_WIDTH);
}

// 绑定事件处理函数
function bindEvent () {
  // 鼠标点到canvas上
  oCan.addEventListener('mousedown', handleCanvasMouseDown, false);
  // 移到端
  oCan.addEventListener('touchstart', handleCanvasMouseDown, false);
  
  // 鼠标点击colorInput
  oColorInput.addEventListener('click', handleColorInput, false);
  oColorInput.addEventListener('input', handleColorInput, false);

  // 拖动数值条
  oLineWidthRange.addEventListener('input', handleLineWidthRangeInput, false);

  // 清除画布
  oClearBtn.addEventListener('click', handleClearBtnClick, false);

  // 点击橡皮擦图标
  oEraserBtn.addEventListener('click', handleEraserBtnClick, false);

  // 改变橡皮擦大小
  oEraserLineWidthRange.addEventListener('input', handleEraserLineWidthRangeInput, false);

  // 键盘事件 撤销与重画
  document.addEventListener('keydown', handleKeyDown, false);

  // 撤销按钮
  oUndoBtn.addEventListener('click', handleUndoBtnclick, false);

  // 重画按钮
  oRedoBtn.addEventListener('click', handleRedoBtnClick, false);
}

// 鼠标按下事件处理函数
function handleCanvasMouseDown (e) {
  // 鼠标按下的坐标
  const x1 = e.clientX || e.touches[0].clientX;
  const y1 = e.clientY || e.touches[0].clientY;

  // 保存初始坐标位置
  initPos = { x1, y1 };
  // console.log(x1, y1);

  // 将点信息存到drewData中
  setDrewRecord('moveTo', [x1, y1]);
  // 撤销按钮可用
  setUndoBtnStyle({ disable: false });

  // 鼠标按下时画点
  drawPoint(x1, y1);


  // 绑定事件处理函数（鼠标移动， 鼠标抬起）
  oCan.addEventListener('mousemove', handleCanvasMouseMove, false);
  oCan.addEventListener('mouseup', handleCanvasMouseUp, false);

  // 移动端
  oCan.addEventListener('touchmove', handleCanvasMouseMove, false);
  oCan.addEventListener('touchend', handleCanvasMouseUp, false);

  if (eraserStatus) {
    // 显示圆圈
    oEraserCircle.setVisible(true);
    oEraserCircle.setPosition(x1, y1);
    // 圆圈显示出来后，鼠标不在canvas上了，而是在圆圈上，所以要在圆圈上绑定鼠标抬起事件处理函数
    oEraserCircle.addEventListener('mouseup', handleEraserCircleMouseUp, false);

    // 移动端
    oEraserCircle.addEventListener('touchend', handleEraserCircleMouseUp, false);
  }
}

// 鼠标移动事件处理函数
function handleCanvasMouseMove (e) {
  
  let x2 = e.clientX || e.touches[0].clientX;
  let y2 = e.clientY || e.touches[0].clientY;

  // ...initPos 是对象， 可以和其他值合并到一个对象中，但不能单独展开
  drawLine({ ...initPos, x2, y2 });

  // 将线信息存档
  setDrewRecord('lineTo', [x2, y2]);
  // 如果是橡皮擦工作状态，就设置圆圈的位置
  eraserStatus && oEraserCircle.setPosition(x2, y2);

  // 给更新初始坐标
  initPos = { x1: x2, y1: y2 }
}

// 鼠标抬起事件处理函数
function handleCanvasMouseUp (e) {
  // 移除鼠标移动事件处理函数
  // 移除鼠标抬起事件处理函数
  oCan.removeEventListener('mousemove', handleCanvasMouseMove, false);
  oCan.removeEventListener('mouseup', handleCanvasMouseUp, false);

  // 移动端
  oCan.removeEventListener('touchmove', handleCanvasMouseMove, false);
  oCan.removeEventListener('touchend', handleCanvasMouseUp, false);
}

// 鼠标在圆圈上时鼠标抬起事件处理函数
function handleEraserCircleMouseUp () {
  // 隐藏圆圈
  oEraserCircle.setVisible(false);
  // 解除事件绑定
  oEraserCircle.removeEventListener('mouseup', handleEraserCircleMouseUp, false);
  // 移动端
  oEraserCircle.removeEventListener('touchend', handleEraserCircleMouseUp, false);
  // 解除canvas上的事件绑定
  handleCanvasMouseUp();
}

// color
function handleColorInput () {
  // 获取颜色值
  const color = this.value;
  // 给canvas设置颜色
  ctx.setColor(color);

}

// lineWidth
function handleLineWidthRangeInput () {
  const lineWidth = this.value;
  oLineWidthRangeValue.textContent = lineWidth;
  ctx.setLineWidth(lineWidth);
}

// 清除画布
function handleClearBtnClick () {
  clearAll();
  drewData = [];
  revokedData = [];
  setUndoBtnStyle({ disable: true});
  setRedoBtnStyle({ disable: true});
  
}

// 橡皮擦
function handleEraserBtnClick (e) {
  const className = e.target.className;
  const lineWidthValue = oEraserLineWidthRange.value;
  if(!className.includes('active')) {
    oEraserBtn.className += ' active';
    eraserStatus = true;
    // 设置橡皮擦圆圈大小
    oEraserCircle.setSize(lineWidthValue);
    ctx.setColor(CANVAS_VALUES.ERASER_COLOR);
    ctx.setLineWidth(lineWidthValue);

  } else {
    oEraserBtn.className = 'icon icon-eraser';
    eraserStatus = false;
    // 隐藏橡皮擦圆圈
    oEraserCircle.setVisible(false);
    // 重新设置线宽
    ctx.setLineWidth(oLineWidthRange.value);
    // 重新设置颜色
    ctx.setColor(oColorInput.value);
  }
  
}

// 改变橡皮擦大小
function handleEraserLineWidthRangeInput () {
  const lineWidth = this.value;
  oEraserLineWidthRangeValue.textContent = lineWidth;
  // 在橡皮擦状态下才能改变
  eraserStatus && ctx.setLineWidth(lineWidth);
  oEraserCircle.setSize(lineWidth);
}

// 键盘事件
function handleKeyDown (e) {
  const key = e.key;
  const isCtrl = e.metaKey || e.ctrlKey;
  // 判断是否按下Ctrl键
  if (isCtrl && Object.values(KEYBOARD).includes(key)) {
    key === KEYBOARD.UNDO && doDrewRecord('undo');
    key === KEYBOARD.REDO && doDrewRecord('redo');
    drawBatchLine();
  }

  // 如果drewData.length为0或revokedData.length为0
  if (!drewData.length || !revokedData.length) {
    ctx.setColor(oColorInput.value);
    ctx.setLineWidth(oLineWidthRange.value);

    if(!drewData.length) {
      setUndoBtnStyle({ disable: true});
    } 
    if (!revokedData.length) {
      setRedoBtnStyle({ disable: true});
    }
  }
}

// 撤销按钮点击事件
function handleUndoBtnclick () {
  doDrewRecord('undo');
  drawBatchLine();
  if(!drewData.length) {
    setUndoBtnStyle({ disable: true});
  } 
}

// 重画按钮点击事件
function handleRedoBtnClick () {
  doDrewRecord('redo');
  drawBatchLine();
  if (!revokedData.length) {
    setRedoBtnStyle({ disable: true});
  }
}

// 画点
function drawPoint (x1, y1) {
  // 开辟路径
  ctx.beginPath();
  // 圆
  ctx.arc(x1, y1, ctx.lineWidth / 2, 2 * Math.PI, false);
  // 填充
  ctx.fill();
}

// 画线
function drawLine ({ x1, y1, x2, y2 }) {
  // 开辟路径
  ctx.beginPath();
  // 画笔先移动到初始位置
  ctx.moveTo(x1, y1);
  // 移动到另一个点，成线
  ctx.lineTo(x2, y2);
  ctx.stroke();
}

// 清除画布
function clearAll () {
  ctx.clearRect(0, 0, oCan.offsetWidth, oCan.offsetHeight);
}

// 橡皮擦圆圈是否显示方法
oEraserCircle.setVisible = function (visible) {
  this.style.display = visible ? 'block' : 'none';
}

// 设置橡皮擦圆圈大小
oEraserCircle.setSize  = function (size) {
  this.style.width = size + 'px';
  this.style.height = size + 'px';
}

// 设置橡皮擦圆圈位置
oEraserCircle.setPosition = function (x, y) {
  this.style.left = x - this.offsetWidth / 2 + 'px';
  this.style.top = y - this.offsetHeight / 2 + 'px';
}

// 设置颜色
ctx.setColor = function (color) {
  this.strokeStyle = color;
  this.fillStyle = color;
}

// 获取颜色
ctx.getColor = function () {
  return ctx.strokeStyle;
}

// 设置线样式
ctx.setLineStyle = function (style) {
  this.lineCap = style;
  this.lineJoin = style; 
}

// 设置线宽
ctx.setLineWidth = function (width) {
  this.lineWidth = width;
}

// 获取线宽
ctx.getLineWidth = function () {
  return ctx.lineWidth;
}

// 设置撤销按钮样式
function setUndoBtnStyle (opt) {
  oUndoBtn.className = opt.disable ?  'icon icon-undo' : 'icon icon-undo active';
}

// 设置重画按钮样式
function setRedoBtnStyle (opt) {
  oRedoBtn.className = opt.disable ? 'icon icon-redo' : 'icon icon-redo active';
}

// 存档
function setDrewRecord (type, data) {
  switch (type) {
    case 'moveTo':
      drewData.push({
        'moveTo': [...data], // x y
        'lineTo': [],
        info: {
          color: ctx.getColor(),
          width: ctx.getLineWidth()
        }
      });
      break;
    case 'lineTo':
      drewData[drewData.length - 1]['lineTo'].push([...data]);
      break;
    default:
      break;
  }
}

// 读档
function doDrewRecord (type) {
  switch (type) {
    case 'undo':
      drewData.length > 0
      &&
      revokedData.push(drewData.pop());
      setRedoBtnStyle({ disable: false});
      break;
    case 'redo':
      revokedData.length > 0
      &&
      drewData.push(revokedData.pop());
      // setUndoBtnStyle({ disable: false });
      break;
    default:
      break;
  }

}

// 重画
function drawBatchLine () {
  clearAll();
  drewData.forEach(item => {
    const {
      moveTo: [x1, y1],
      lineTo,
      info: { color, width }
    } = item;
    ctx.beginPath();
    ctx.setColor(color);
    ctx.setLineWidth(width);
    ctx.moveTo(x1, y1);
  
    lineTo.forEach(line => {
      ctx.lineTo(...line);
    });
    ctx.stroke();
  })
}

init();